using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace FrameWork.AStar
{
    public class AStartManager
    {
        private int mapWidth;                       // 地图宽
        private int mapHeight;                      // 地图高
        
        private AStarNode[,] nodes;
        public AStarNode[,] GetNodes{get{return nodes;}}
        
        private List<AStarNode> openList;           // 开启列表
        private List<AStarNode> closedList;         // 关闭列表

        /// <summary>
        /// 初始化地图大小
        /// </summary>
        /// <param name="mapWidth"></param>
        /// <param name="mapHeight"></param>
        public void InitMapSizeInfo(int mapWidth, int mapHeight)
        {
            nodes = new AStarNode[mapWidth, mapHeight];
            this.mapWidth = mapWidth;
            this.mapHeight = mapHeight;
            openList = new List<AStarNode>();
            closedList = new List<AStarNode>();
        }

        public void InitMapInfo(int x, int y, float weight, AStar_Node_Type nodeType)
        {
            AStarNode node = new AStarNode(x, y, weight, nodeType);
            nodes[x, y] = node;
        }

        /// <summary>
        /// 寻路
        /// </summary>
        /// <param name="startPoint">起点</param>
        /// <param name="endPoint">终点</param>
        /// <returns></returns>
        public List<AStarNode> FindPath(Vector2 startPoint, Vector2 endPoint)
        {
            // 判断是否在地图内
            if (startPoint.x < 0 || startPoint.x > mapWidth || startPoint.y < 0 || startPoint.y > mapHeight
                || endPoint.x < 0 || endPoint.x > mapWidth || endPoint.y < 0 || endPoint.y > mapHeight)
            {
                Debug.LogError("起点或者终点不在地图范围内" + startPoint + endPoint);
                return null;
            }
            
            // 判断是否为障碍物
            AStarNode start = nodes[(int)startPoint.x, (int)startPoint.y];
            AStarNode end = nodes[(int)endPoint.x, (int)endPoint.y];

            if (start.type == AStar_Node_Type.Stop || end.type == AStar_Node_Type.Stop)
            {
                Debug.LogError("起点或者终点为障碍物");
                return null;
            }
            
            // 清空列表
            openList.Clear();
            closedList.Clear();
            
            //
            start.parentNode = null;
            start.f = 0;
            start.g = 0;
            start.h = 0;
            closedList.Add(start);

            while (true)
            {
                // 查找临近的四个点，防止玩家斜向移动
                // 查找临近点 左右-- -/+  上下-- -/+
                // 左上
                // FindNearlyToOpenList(start.x - 1, start.y - 1, 1.4f, start, end);
                // 上
                FindNearlyToOpenList(start.x, start.y - 1, 1f, start, end);
                // 右上
                // FindNearlyToOpenList(start.x + 1, start.y - 1, 1.4f, start, end);
                // 左
                FindNearlyToOpenList(start.x - 1, start.y, 1f, start, end);
                // 右
                FindNearlyToOpenList(start.x + 1, start.y, 1f, start, end);
                // 左下
                // FindNearlyToOpenList(start.x - 1, start.y + 1, 1.4f, start, end);
                // 下
                FindNearlyToOpenList(start.x, start.y + 1, 1f, start, end);
                // 右下
                // FindNearlyToOpenList(start.x + 1, start.y + 1, 1.4f, start, end);

                // 死路判断
                if (openList.Count == 0)
                {
                    Debug.LogError("死路");
                    return null;
                }
            
                // 开启列表排序，选择消耗最少的点
                openList.Sort(SortOpenList);
            
                //
                closedList.Add(openList[0]);
                start = openList[0];
            
                openList.RemoveAt(0);

                if (start == end)
                {
                    List<AStarNode> path = new List<AStarNode>();
                    path.Add(end);
                    while (end.parentNode != null)
                    {
                        path.Add(end.parentNode);
                        end = end.parentNode;
                    }
                    
                    path.Reverse();
                    
                    // 测试语句
                    // for (int i = 0; i < path.Count; i++)
                    // {
                    //     Debug.Log("寻路路径" + i + path[i]);
                    // }
                    
                    return path;
                }
            }
        }
        
        /// <summary>
        /// 排序函数
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        private int SortOpenList(AStarNode a, AStarNode b)
        {
            if (a.f > b.f)
                return 1;
            else if (a.f == b.f)
                return 1;
            else
                return -1;
        }

        private void FindNearlyToOpenList(int x, int y, float g, AStarNode parentNode, AStarNode endNode)
        {
            // 边界判断
            if (x < 0 || x >= mapWidth || y < 0 || y >= mapHeight)
                return;
            
            AStarNode node = nodes[x, y];
            // 是否是边界和障碍物，以及是否在列表中
            if(node == null || node.type == AStar_Node_Type.Stop || closedList.Contains(node) || openList.Contains(node))
                return;
            
            // 计算 f 值
            // 记录父节点
            node.parentNode = parentNode;
            node.g = parentNode.g + g;
            node.h = Mathf.Abs(endNode.x - node.x) + Mathf.Abs(endNode.y - node.y);
            node.f = node.g + node.h + node.weight;
            
            // 加入OpenList
            openList.Add(node);
        }
    }
}
